import datetime
import hashlib
import os
import re

from Extend.GrandClass import GrandClass
from Extend.Log import *
from Extend.FileMethod import FileExtend
from Extend.NoSql import SharedData
import time
from Extend.ListAdvancedMethod import ListMethod
from Extend.NoSql import Heart
# import threading
import gevent


# 对方服务器的实体类
class ServerModel:
    def __init__(self, server, path):
        # 服务器地址，用作特征
        self.__server = server
        # 服务器存放文件的路径
        self.__path = os.path.join(r'\\' + server, path)
        self.lash_md5 = ''

    def readTodayLog(self):
        file = os.path.join(self.__path, time.strftime("%F", time.localtime(time.time())) + ".log")
        if self.fileIsChange(file):
            self.readLogs(file)

    def readYesterdayLog(self):
        file = os.path.join(self.__path, (datetime.datetime.now() - datetime.timedelta(days=1)).strftime('%F') + ".log")
        self.readLogs(file)

    def readLogs(self, file):
        if os.path.exists(file):
            content = FileExtend.readFile(file)
            ToSQL.contentFacory(content, self.__server)
        else:
            name = os.path.basename(file)
            raise Exception('%s日志文件不存在!' % name[0:name.index('.')])

    def fileIsChange(self, path):
        with open(path, 'rb') as f:
            byte_content = f.read()
        file_md5 = hashlib.md5(byte_content).hexdigest()
        if self.lash_md5 != file_md5:
            self.lash_md5 = file_md5
            return True
        else:
            return False

    @property
    def server(self):
        return self.__server

    @property
    def path(self):
        return self.__path


class ToSQL:
    @staticmethod
    def contentFacory(content, server):
        line_content = content.split('\n')
        for line in line_content:
            temp_line = line.split('\t')
            if len(temp_line) != 3:
                continue
            temp = {"OrderNO": temp_line[0], "CreateDate": temp_line[2], "Server": server}
            ToSQL.add(temp_line.pop(1), temp)

    @staticmethod
    def add(stat, val_list):
        value_date = val_list['CreateDate']
        value_date = value_date[0:value_date.index(' ')].replace('/', '-')
        value_date = re.sub(r'\w+', lambda ele: ele.group().zfill(2), value_date)
        key = "{}_{}".format(value_date, stat)
        result = SharedData.GetList(key)
        if val_list not in result:
            SharedData.PushVal(key, val_list)


class FileMonitor(GrandClass):
    """文件监控主逻辑，用来分配目标服务器地址，需要传入一个列表，列表中包含服务的地址"""

    def __init__(self, server_list: list):
        super().__init__()
        self.server_list = []
        self.offline_server = []
        self.recovery_server = []
        self.__addServer(server_list)

    def __addServer(self, server_list: list):
        for x in server_list:
            self.server_list.append(ServerModel(x['Server'], x['LogPath']))

    def _recoveryServer(self, server):
        for x in self.server_list + self.recovery_server:
            if x is server:
                return
        self.recovery_server.append(server)

    def recoveryServerToRun(self):
        for x in range(-len(self.recovery_server) + 1, 1):
            server = self.recovery_server.pop(abs(x))
            self.server_list.append(server)
            LogMethod.addLog([5, "服务器:%s 已经恢复读取" % server.server, ""])

    def addServer(self, server_list: list):
        self.clearServeList()
        self.__addServer(server_list)

    # 循环主逻辑
    def startMonitors(self):
        while Heart.getMeHeart(self.__class__.__name__, ):
            super().startMonitors()
            if len(self.recovery_server) > 0:
                self.recoveryServerToRun()
                continue
            if len(self.server_list) == 0:
                gevent.sleep(2)
                continue
            for server_name in self.contrastDifferences():
                for server in self.server_list:
                    if server_name == server.server:
                        try:
                            server.readYesterdayLog()
                        except Exception as err:
                            self.removeServer(server, err)
            for x in self.server_list:
                try:
                    x.readTodayLog()
                except Exception as err:
                    self.removeServer(x, err)
            gevent.sleep(1)  # 工作太快协程会不检测服务器状态
        Heart.setMeStat(self.__class__.__name__, False)

    def removeServer(self, server, error):
        for i in range(len(self.server_list)):
            if self.server_list[i] is server:
                self.server_list.pop(i)
                break
        LogMethod.addLog([4, "服务器:%s 无法读取文件" % server.server, str(error)])
        gevent.spawn(MonitorM.heartbeatDetection, server, self._recoveryServer)

    def clearServeList(self):
        for i in range(len(self.server_list)):
            server = self.server_list.pop(i)
            del server

    def contrastDifferences(self):
        yes_list = []
        for x in range(3):
            yes_list.append(SharedData.GetYesterday(x))
        if len(yes_list[0]) - len(yes_list[1]) - len(yes_list[2]) > 0:
            return ListMethod.getDifference(yes_list[0], yes_list[1] + yes_list[2], return_key='Server',
                                            ignore_key='CreateDate')
        else:
            return []


# 监测服务器类
class MonitorM:

    @staticmethod
    def heartbeatDetection(obj: ServerModel, return_func, loop=0):
        while True:
            try:
                file = os.path.join(obj.path, time.strftime("%F", time.localtime(time.time())) + ".log")
                if os.path.exists(file):
                    break
                else:
                    raise IOError
            except Exception as e:
                loop += 1
                if loop <= 50:
                    gevent.sleep(5)
                elif loop <= 100:
                    gevent.sleep(10)
                else:
                    gevent.sleep(20)
        return_func(obj)


if __name__ == '__main__':
    servers = [{'Server': "172.16.200.21", 'LogPath': "LogFiles"},
               {'Server': "172.16.200.252", 'LogPath': "LogFiles"}]
    # LogMethod.addServerList(servers, r'F:\CloudMusic')
    # LogMethod.startServer()
    # distributor = FileMonitor(servers, r'F:\CloudMusic')
    # distributor.start()
    distributor = FileMonitor(servers)
    distributor.start()
